﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharpSoft.WPF.WriteableBitmapModules
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;

    /// <summary>
    /// 爆炸动画
    /// </summary>
    public class BoomAni : WriteableBitmapModules.AnimationModuleBase
    {
        public BoomAni()
        {
        }
        #region 初始信息

        /// <summary>
        /// 要进行动画的图像源
        /// </summary>
        public BitmapSource Source { get; set; }
        /// <summary>
        /// 源图像的初始位置
        /// </summary>
        public PointInt32 InitialPosition { get; set; }
        /// <summary>
        /// 动画后的目标位置
        /// </summary>
        public PointInt32 TargetPosition { get; set; }
        /// <summary>
        /// 分解图像源的网格信息
        /// </summary>
        public GridInfo GridInfo { get; set; } = new GridInfo(10);
        #endregion
        #region 速度控制
        /// <summary>
        /// 加速度
        /// </summary>
        public int Acceleration { get; set; } = 400;
        /// <summary>
        /// 减速度
        /// </summary>
        public int Deceleration { get; set; } = 200;
        /// <summary>
        /// 最大移动速度达到这个速度后开始减速）
        /// </summary>
        public int MaxVelocity { get; set; } = 500;
        /// <summary>
        /// 最小速度
        /// </summary>
        public int MinVelocity { get; set; } = 200;
        /// <summary>
        /// 速度的随机种子，表示速度将随机降低最多这个值
        /// </summary>
        public int VelocityRandomSeed { get; set; } = 100;
        /// <summary>
        /// 爆炸半径
        /// </summary>
        public int BoomRadius { get; set; } = 500;

        /// <summary>
        /// 源图像的爆炸中心，相对位置，为null表示无中心，各个碎片往随机方向运动。(默认为源图像的正中心0.5,0.5)
        /// </summary>
        public Point? BoomCenter { get; set; } = new Point(0.5, 0.5);
        #endregion
        private Random random = new Random();
        private double velocity = 0d;//当前速度
        private WriteableBitmap[] pieces;//拆分后的碎片
        private Point[] points;//每个碎片的位置
        private Vector[] directions;//每个碎片的方向
        private double[] velocities;//每个碎片的速度
        private int pieceWidth, pieceHeight;//碎片的尺寸

        bool allStatic = false;//全部碎片已静止
        int velocityStatus = 0;//速度状态 1正在加速2正在减速
        TimeSpan boomTime;//根据加速度和爆炸半径计算出爆炸需要的时间
        /// <summary>
        /// 根据设置拆分源图像
        /// </summary>
        private void Split()
        {
            if (this.Source != null)
            {
                if (this.Source.PixelWidth < this.GridInfo.Columns || this.Source.PixelHeight < this.GridInfo.Rows)
                {
                    throw new Exception($"{Source.PixelWidth}x{Source.PixelHeight}的图像不足以分割成{GridInfo.Columns}x{GridInfo.Rows}块。");
                }
                var bmp = new WriteableBitmap(this.Source);
                pieceWidth = this.Source.PixelWidth / GridInfo.Columns;
                pieceHeight = this.Source.PixelHeight / GridInfo.Rows;
                var count = GridInfo.Rows * GridInfo.Columns;
                pieces = new WriteableBitmap[count];
                points = new Point[count];
                directions = new Vector[count];
                velocities = new double[count];
                Rect souRect = new Rect(0, 0, pieceWidth, pieceHeight);
                Rect tarRect = new Rect(0, 0, pieceWidth, pieceHeight);

                for (int r = 0; r < GridInfo.Rows; r++)
                {
                    for (int c = 0; c < GridInfo.Columns; c++)
                    {
                        var i = r * GridInfo.Columns + c;
                        souRect.X = pieceWidth * c;
                        souRect.Y = pieceHeight * r;
                        var piece = new WriteableBitmap(pieceWidth, pieceHeight, bmp.DpiX, bmp.DpiY, bmp.Format, bmp.Palette);
                        piece.Lock();
                        piece.Blit(tarRect, bmp, souRect);
                        piece.Unlock();
                        pieces[i] = piece;
                        velocities[i] = 1;
                    }
                }
                this.ResetMovement();
            }
            else
            {
                throw new Exception("未指定动画的图像源。");
            }
        }

        /// <summary>
        /// 重置运动状态（回到初始位置，计算初始速度）
        /// </summary>
        private void ResetMovement()
        {
            Point pieceCenter = new Point();
            RotateTransform rotate = new RotateTransform();
            for (int r = 0; r < GridInfo.Rows; r++)
            {
                for (int c = 0; c < GridInfo.Columns; c++)
                {
                    var i = r * GridInfo.Columns + c;

                    points[i] = new Point(this.InitialPosition.X + pieceWidth * c, this.InitialPosition.Y + pieceHeight * r);
                    Vector v;
                    if (BoomCenter.HasValue)
                    {
                        pieceCenter.X = (c * pieceWidth + pieceWidth / 2d) / this.Source.PixelWidth;
                        pieceCenter.Y = (r * pieceHeight + pieceHeight / 2d) / this.Source.PixelHeight;
                        v = pieceCenter - BoomCenter.Value;
                    }
                    else
                    {
                        rotate.Angle = random.Next(360);
                        v = new Vector(1, 0) * rotate.Value;
                    }




                    v.Normalize();

                    directions[i] = v;
                    velocities[i] = random.Next(VelocityRandomSeed);//给定随机的初始速度


                }
            }
        }
        public override void Initialize()
        {
            if (this.MaxVelocity < this.MinVelocity)
            {
                throw new Exception("最大速度不能小于最小速度.");
            }
            if (this.VelocityRandomSeed < 0)
            {
                throw new Exception("速度随机种子不能小于0.");
            }
            if (this.VelocityRandomSeed >= this.MinVelocity)
            {
                throw new Exception("速度随机种子不能大于或等于所设定的最低速度.");
            }
            var at = this.MaxVelocity / this.Acceleration;//加速到最高速度所需要的时间
            var t = Math.Sqrt(BoomRadius * 2 / Acceleration);
            if (at < t)
            {//还没到最高速度就完成路径
                boomTime = TimeSpan.FromSeconds(t);
            }
            else
            {
                var s1 = this.Acceleration * Math.Pow(at, 2);
                var s2 = BoomRadius - s1;
                boomTime = TimeSpan.FromSeconds(at + s2 / this.MaxVelocity);
            }
            this.velocity = this.MinVelocity;
            this.Split();

            base.Initialize();
        }
        public override void Start()
        {
            if (!this.IsInitialized)
            {
                this.Initialize();
            }
            this.velocity = MinVelocity;
            this.velocityStatus = 1;
            this.Stop();
            allStatic = false;//开始运动，标记
            this.ResetMovement();
            stopWatch.Reset();
            base.Start();
        }

        public override void Stop()
        {
            allStatic = true;//标记停止运动
            base.Stop();

        }

        protected override void OnUpdate(AnimationTime time)
        {
            base.OnUpdate(time);
            //计算速度
            if (velocityStatus == 1)
            {//加速
                if (this.velocity < this.MaxVelocity)
                {
                    this.velocity += this.Acceleration * time.ElapsedTime.TotalSeconds;
                    for (int r = 0; r < GridInfo.Rows; r++)
                    {
                        for (int c = 0; c < GridInfo.Columns; c++)
                        {
                            var i = r * GridInfo.Columns + c;

                            velocities[i] += (this.Acceleration * time.ElapsedTime.TotalSeconds);
                            velocities[i] = Math.Min(velocities[i], MaxVelocity);
                        }
                    }
                }
                else
                {
                    this.velocity = this.MaxVelocity;
                }

            }
            else if (velocityStatus == 2)
            {//减速
                if (this.velocity > this.MinVelocity)
                {
                    this.velocity -= this.Acceleration * time.ElapsedTime.TotalSeconds;
                    for (int r = 0; r < GridInfo.Rows; r++)
                    {
                        for (int c = 0; c < GridInfo.Columns; c++)
                        {
                            var i = r * GridInfo.Columns + c;

                            velocities[i] -= (this.Acceleration * time.ElapsedTime.TotalSeconds);
                            velocities[i] = Math.Max(velocities[i], MinVelocity);
                        }
                    }
                }
                else
                {
                    this.velocity = this.MinVelocity;
                }
            }
            //计算新的位置
            for (int r = 0; r < GridInfo.Rows; r++)
            {
                for (int c = 0; c < GridInfo.Columns; c++)
                {
                    var i = r * GridInfo.Columns + c;
                    var v = directions[i];
                    var p = points[i];
                    p = (p + v * velocities[i] * time.ElapsedTime.TotalSeconds);
                    points[i] = p;
                }
            }

            if (time.TotalTime >= this.boomTime)
            {//爆炸结束 ,动态计算速度向量
                this.velocityStatus = 2;//爆炸结束开始减速
                bool isAllStatic = true;
                for (int r = 0; r < GridInfo.Rows; r++)
                {
                    for (int c = 0; c < GridInfo.Columns; c++)
                    {
                        var i = r * GridInfo.Columns + c;
                        //目标位置
                        var targetP = new Point(this.TargetPosition.X + pieceWidth * c, this.TargetPosition.Y + pieceHeight * r);
                        var p = points[i];
                        var v = targetP - p;
                        if (v.Length < velocities[i] / 30d)
                        {//距离目标位置已经小于30帧率时一帧的距离,设该碎片静止
                            points[i] = targetP;
                            directions[i] = new Vector(0, 0);
                        }
                        else
                        {
                            v.Normalize();
                            directions[i] = v;//计算新的方向
                            isAllStatic = false;
                        }

                    }
                }
                allStatic = isAllStatic;
            }

        }

        protected override void OnRender(AnimationTime time)
        {
            var rt = this.RenderTarget;
            if (rt != null && pieces != null)
            {
                using (rt.GetBitmapContext())
                {
                    rt.Clear(Colors.Transparent);
                    Rect souRect = new Rect(0, 0, pieceWidth, pieceHeight);
                    Rect tarRect = new Rect(0, 0, pieceWidth, pieceHeight);
                    for (int r = 0; r < GridInfo.Rows; r++)
                    {
                        for (int c = 0; c < GridInfo.Columns; c++)
                        {
                            var i = r * GridInfo.Columns + c;
                            var piece = pieces[i];
                            var point = points[i];
                            tarRect.X = point.X;
                            tarRect.Y = point.Y;
                            rt.Blit(tarRect, piece, souRect);
                        }
                    }
                }
            }
            base.OnRender(time);
            if (allStatic)
            {
                this.OnCompleted();
            }
        }
        protected void OnCompleted()
        {
            this.Stop();
            this.Completed?.Invoke(this, EventArgs.Empty);
        }
        /// <summary>
        /// 在动画完成时触发
        /// </summary>
        public event EventHandler Completed;

    }
   
}
